package com.androdro.bluenote.coolstruttin1588;
/**
 * Cool Struttin'
 * Lover.java
 *
 * @author Hiroaki Sakuma <sakuma@androdro.com>
 * @version $Id$
 *
 * Copyright (C) androdro lab. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of the nor the names of its contributors may be used to
 *   endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 */

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.Runnable;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import android.content.Context;
import android.net.Uri;
import android.net.http.AndroidHttpClient;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.widget.Toast;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.androdro.bluenote.coolstruttin1588.utility.Define;

public class Lover
	implements
		Runnable {

	private CoolStruttin root;
	private static Lover me;

	private DisplayMetrics metrics;
	private JSONObject jsonObj = null;
	private Thread t;


	private Lover() {}

	private Lover(CoolStruttin root) {

		this.root = root;

		try {

			this.metrics = this.root.getDisplayMetrics();

		} catch (Exception e) {
			e.printStackTrace();
			Toast.makeText(this.root, e.toString() + "on " + getClass().toString() + " " + e.getStackTrace()[0].getMethodName(), Toast.LENGTH_LONG).show();
		}

	}


	public static synchronized Lover getInstance(CoolStruttin root) {
		if (me == null) {
			me = new Lover(root);
		}
		me.start();
		return me;
	}


	public Object getLockObject() {
		return this.t;
	}


	private void start() {

		if (this.t == null) {
			this.t = new Thread(this);
			this.t.start();
		}

	}


	public static void halt() {
System.out.println("halt on Lover");
//		me.jsonObj = null;
//		me.t = null;
	}


	public void run() {

		SippinAtBells.DecryptAES decryptor = SippinAtBells.getInstance().getFactory().getDecryptor();
		AndroidHttpClient client = null;
		byte[] jsonenc;
		String json = null;
		Thread t = Thread.currentThread();
		Handler h = root.getHandler();

		try {

			Uri.Builder builder = new Uri.Builder();
			builder.scheme("http");
			builder.encodedAuthority(Define.URL.URL_API_CONFIG_HOST);
			builder.path(Define.URL.URL_API_CONFIG_PATH + ".b64");
			builder.appendQueryParameter("MANUFACTURER",    Build.MANUFACTURER);
			builder.appendQueryParameter("MODEL",           Build.MODEL);
			builder.appendQueryParameter("BRAND",           Build.BRAND);
			builder.appendQueryParameter("DEVICE",          Build.DEVICE);
			builder.appendQueryParameter("PRODUCT",         Build.PRODUCT);
			builder.appendQueryParameter("VERSION.RELEASE", Build.VERSION.RELEASE);
			builder.appendQueryParameter("VERSION.SDK",     Build.VERSION.SDK);
			builder.appendQueryParameter("VERSION.RELEASE", Build.VERSION.RELEASE);
			builder.appendQueryParameter("DISPLAY.WIDTH",   Integer.toString(metrics.widthPixels));
			builder.appendQueryParameter("DISPLAY.HEIGHT",  Integer.toString(metrics.heightPixels));
			builder.appendQueryParameter("DISPLAY.DENSITYDPI",  Integer.toString(metrics.densityDpi));

			synchronized(t) {

				for(;jsonObj == null && json == null;) {
//				for(;jsonObj == null && json == null && !this.root.isFinishing() && !t.isInterrupted();) {

					HttpGet request = new HttpGet(builder.build().toString());
					request.addHeader("Referer", Define.URL.URL_HTTP_DEFAULT_REFERER);

					if (client == null) {
						client = AndroidHttpClient.newInstance(Define.URL.HTTP_USER_AGENT, root);
					}

					try {

						this.root.printConsole("Fetching data from server.");

						jsonenc = client.execute(request, new ResponseHandler<byte[]>() {
							public byte[] handleResponse(HttpResponse response)
							throws
							ClientProtocolException,
							IOException {
								switch(response.getStatusLine().getStatusCode()) {
								case HttpStatus.SC_OK:
									return EntityUtils.toByteArray(response.getEntity());
								case HttpStatus.SC_NOT_FOUND:
									throw new RuntimeException(EntityUtils.toString(response.getEntity(), "UTF-8"));
								default:
									throw new RuntimeException(EntityUtils.toString(response.getEntity(), "UTF-8"));
								}
							}
						});

						this.root.printConsole("OK! Fetched config.");
						this.root.printConsole("Decrypt config.");

						json = decryptor.decrypt(Base64.decode(jsonenc, Base64.DEFAULT), "TestSecretKey");
//System.out.println(json);
						OutputStream out = this.root.openFileOutput("config.json.b64", Context.MODE_WORLD_READABLE);
						out.write(jsonenc);
						out.close();

						root.printConsole("3 2 1 ...");

					} catch(UnknownHostException uhe) {
						uhe.printStackTrace();
						final String error = uhe.toString();
						h.post(
							new Runnable() {
								public void run() {
									root.showError(error);
								}
							}
						);
						throw uhe;
					} catch(Exception e) {
						e.printStackTrace();
					} finally {
						client.getConnectionManager().shutdown();
						client.close();
					}

					if (json != null) {

						jsonObj = new JSONObject(json);

						root.printConsole("Let's JAM JAM!!");

					} else {
						Thread.sleep(1000);
					}

				} //for

				t.notify();

			} //synchronized

		} catch (Exception e) {
			e.printStackTrace();
		}


	}


	public synchronized JSONObject getConfig() {

		if(jsonObj == null) {

			synchronized(this.t) {

				if (this.t.isAlive()) {

					try {
						this.t.wait();
						this.t.interrupt();
						this.t.join(1000);
					} catch(InterruptedException ex) {
						ex.printStackTrace();
					} catch(Exception e) {
						e.printStackTrace();
					}

				}

			} //synchronized

//			this.t = null;

		}

		return jsonObj;

	}


	public synchronized Map<String, Object> getHighways() {

		Map<String, Object> r = new TreeMap<String, Object>(new Lover.HighwaysComparator<String>());

		try {
			JSONArray highways = getConfig().getJSONObject("Body").getJSONArray("Highways");
			int l = highways.length();
			for (int i = 0; i < l; i++) {
				JSONObject v = highways.getJSONObject(i);
				String k = v.getString("Name");
				r.put(k, v);
			}

		} catch (JSONException jsone) {
			jsone.printStackTrace();
		}

		return r;

	}


	public JSONObject getHighway(String name) {

		JSONObject r = null;

		Map<String, Object> highways = getHighways();
		r = (JSONObject)highways.get(name);

		return r;

	}


	public Map<String, Object> getCheckPoints(String kind, String name) {

		Map<String, Object> r = new TreeMap<String, Object>(new Lover.HighwaysComparator<String>());

		try {
			JSONArray highways = getConfig().getJSONObject("Body").getJSONArray(kind);
			int l = highways.length();
			for (int i = 0; i < l; i++) {
				JSONObject v = highways.getJSONObject(i);
				String k = v.getString("Name");
				if (k.equals(name)) {
					Iterator i2 = v.getJSONObject("CheckPoints").keys();
					while(i2.hasNext()) {
						JSONObject v2 = (JSONObject)i2.next();
						String k2 = v2.getString("Line") + "," + v2.getString("Name");
						r.put(k2, v2);
					}
					break;
				}
			}


		} catch (JSONException jsone) {
			jsone.printStackTrace();
		}

		return r;

	}


	public String getHighwayName(String name) {

		String r = null;

		try {
			r = getHighway(name).getString("Name");
		} catch (JSONException jsone) {
			jsone.printStackTrace();
		}

		return r;

	}


	public String getHighwayImageURL(String name) {

		String r = new String();

		try {
/*
			JSONArray highways = getConfig().getJSONObject("Body").getJSONArray("Highways");
			int l = highways.length();
			for (int i = 0; i < l; i++) {
				JSONObject v = highways.getJSONObject(i);
				String k = v.getString("Name");
				if (k.equals(name)) {
					r = v.getString("ImageURL");
					break;
				}
			}
*/
			r = getHighway(name).getString("ImageURL");
		} catch (JSONException jsone) {
			jsone.printStackTrace();
		}

		return r;

	}


	public class LoverUncaughtExceptionHandler
		implements
			UncaughtExceptionHandler {


		public void uncaughtException(Thread t, Throwable throwable) {
			throwable.printStackTrace();
			root.showError(throwable.toString());
		}


	}


	public class HighwaysComparator<T>
		implements
			Comparator,
			Serializable {


		ArrayList<String> highways;


		public <T> HighwaysComparator() {

			this.highways = new ArrayList<String>();

			try {

				JSONArray highways = getConfig().getJSONObject("Body").getJSONArray("Highways");
				int l = highways.length();
				for (int i = 0; i < l; i++) {
					JSONObject v = highways.getJSONObject(i);
					this.highways.add(v.getString("Name"));
				}

			} catch (Exception e) {
				e.printStackTrace();
			}

		}


		public int compare(Object o1, Object o2) {

			int p1 = this.highways.indexOf(o1);
			int p2 = this.highways.indexOf(o2);

			if (p1 == -1 && p2 == -1) {
				return -1; // ... => o1 => o2 => ...
			}

			if (p1 == p2) {
				return 0;
			}

			if (p1 == -1) {
				return 1; // ... => o2 => o1 => ...
			}

			if (p2 == -1) {
				return -1; // ... => o1 => o2 => ...
			}

			if (p1 < p2) {
				return -1; // ... => o1 => o2 => ...
			}

			if (p1 > p2) {
				return 1; // ... => o2 => o1 => ...
			}

			return -1;

		}


	}


}
